//
// Copyright (c) 2009 All Right Reserved
//
// vl
//
// 2009-01-01
// Contains ...
using LargoCommon.Interfaces;
using System;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
namespace LargoCommon.Music
{
/// Harmonic cluster.
///
/// Harmonic cluster is a set of tones representing a cross-section of voices in given time point.
/// It is designed for check of tone doubling and for computation of actual Ambit, density,
/// measure of dissonance, potential,...
[Serializable]
//// [XmlInclude(typeof(MusicalTone))]
[XmlRoot]
public sealed class HarmonicCluster : IHarmonic {
#region Fields
/// List of tones.
private readonly MusicalToneCollection toneList;
///
/// Harmonic system.
///
private HarmonicSystem harSystem;
///
/// Harmonic structure.
///
private HarmonicStructure harStruct;
/// List of tones.
private MusicalToneCollection validToneList; //// MusicalToneCollection
/// String of musical symbols.
private string toneSchema;
///
/// Real harmonic state of the cluster.
///
private HarmonicStateReal realHarmonicState;
///
/// Formal harmonic state of the cluster.
///
private HarmonicStateFormal formalHarmonicState;
#endregion
#region Constructors
/// Initializes a new instance of the HarmonicCluster class. Serializable.
public HarmonicCluster()
{
this.FormalEnergy = new HarmonicBehavior();
this.RealEnergy = new HarmonicBehavior();
}
/// Initializes a new instance of the HarmonicCluster class.
/// Harmonic structure.
/// Rhythmical tick.
/// Rhythmical duration.
public HarmonicCluster(HarmonicStructure harmonicStructure, byte tick, byte duration) : this()
{
this.HarmonicStructure = harmonicStructure;
this.Tick = tick;
this.Duration = duration;
this.toneList = new MusicalToneCollection();
}
#endregion
#region Properties
///
/// Gets or sets the bar number.
///
/// The bar number.
public int BarNumber { get; set; }
/// Gets harmonic system.
/// Property description.
[XmlIgnore]
public HarmonicSystem HarmonicSystem {
get {
Contract.Ensures(Contract.Result() != null);
if (this.harSystem == null) {
throw new InvalidOperationException("Harmonic system is null.");
}
return this.harSystem;
}
}
/// Gets or sets harmonics structure corresponding to the cluster.
/// Property description.
public HarmonicStructure HarmonicStructure {
get => this.harStruct;
set {
this.harStruct = value;
if (value != null) {
this.harSystem = value.HarmonicSystem;
}
}
}
/// Gets or sets first tick of the cluster.
/// Property description.
public byte Tick { get; set; }
/// Gets or sets duration of the cluster.
/// Property description.
public byte Duration { get; set; }
/// Gets or sets current effective length of the cluster.
/// Property description.
public byte CurrentEffectiveLength { get; set; }
///
/// Gets the formal energy.
///
/// Property description.
public HarmonicBehavior FormalEnergy { get; }
///
/// Gets the real energy.
///
/// Property description.
public HarmonicBehavior RealEnergy { get; }
/// Gets or sets the property.
/// Property description.
public float IndexOfCentre { get; set; }
/// Gets or sets the property.
/// Property description.
public float Weight { get; set; }
/// Gets or sets the property.
/// Property description.
public int Ambit { get; set; }
/// Gets or sets the property.
/// Property description.
public int FormalAmbit { get; set; }
/// Gets or sets the property.
/// Property description.
public float Density { get; set; }
/// Gets list of all tones.
/// Property description.
public MusicalToneCollection ToneList {
get {
Contract.Ensures(Contract.Result() != null);
if (this.toneList == null) {
throw new InvalidOperationException("List of tones is null.");
}
return this.toneList;
}
}
/// Gets list of all already defined tones.
/// Property description.
public MusicalToneCollection ValidToneList {
get {
Contract.Ensures(Contract.Result() != null);
if (this.validToneList == null) {
throw new InvalidOperationException("Valid tone list must not be null.");
}
return this.validToneList;
}
}
///
/// Gets real harmonic state of the cluster.
///
/// Property description.
public HarmonicStateReal RealHarmonicState {
get
{
Contract.Ensures(Contract.Result() != null);
return this.realHarmonicState ??
(this.realHarmonicState = new HarmonicStateReal(this.HarmonicSystem, this.ValidToneList));
}
}
///
/// Gets formal harmonic state of the cluster.
///
/// Property description.
public HarmonicStateFormal FormalHarmonicState {
get {
Contract.Ensures(Contract.Result() != null);
if (this.formalHarmonicState != null) {
return this.formalHarmonicState;
}
if (this.HarmonicStructure == null) {
throw new InvalidOperationException("Harmonic structure is null.");
}
this.formalHarmonicState = new HarmonicStateFormal(this.HarmonicSystem, this.HarmonicStructure);
return this.formalHarmonicState;
}
}
/// Gets cluster duration.
/// Property description.
public HarmonicClusterExtent Extent { get; private set; }
/// Gets or sets string of musical tone symbols.
/// Property description.
[XmlAttribute]
public string ToneSchema {
get => this.toneSchema ?? (this.toneSchema = this.ToneList.ToneSchema);
set => this.toneSchema = value;
}
///
/// Gets a value indicating whether Is chord.
///
/// Property description.
public bool IsChord => this.IsValid && this.ValidToneList.Count > 1 && this.harSystem != null;
///
/// Returns true if ... is valid.
///
///
/// true if this instance is valid; otherwise, false.
///
public bool IsValid => this.validToneList != null;
#endregion
#region Cluster tones
/// Adds tone to the cluster.
/// Melodic tone.
public void AddMelodicTone(MusicalTone givenTone) {
this.ToneList.Add(givenTone);
}
/// Returns all the harmonic tones.
/// Returns value.
public MusicalToneCollection HarmonicTones() {
if (this.HarmonicStructure == null) {
return null;
}
var hs = this.HarmonicStructure;
var tones = from mt in this.ValidToneList
where hs.IsOn(mt.Pitch.Element)
select mt;
var collection = new MusicalToneCollection(tones, false);
return collection; //// new MusicalToneCollection(tones.ToList());
}
/// Returns all the non-harmonic tones.
/// Returns value.
public MusicalToneCollection MelodicTones() {
if (this.HarmonicStructure == null) {
return null;
}
var hs = this.HarmonicStructure;
var tones = from mt in this.ValidToneList
where hs.IsOff(mt.Pitch.Element)
select mt;
var collection = new MusicalToneCollection(tones, false);
return collection; //// new MusicalToneCollection(list.ToList());
}
/// Returns number of tones corresponding to given formal element.
/// Pitch element.
/// Returns value.
public int NumberOfEqualTones(byte givenPitchElement) {
var number = (from melTone in this.ToneList
where melTone.IsTrueTone && (melTone.Pitch.Element == givenPitchElement)
select 1)
.Count();
return number;
}
///
/// Numbers the of mel tones.
///
/// Returns value.
public int NumberOfTrueTones() {
var number = (from melTone in this.ToneList
where melTone.IsTrueTone
select 1)
.Count();
return number;
}
#endregion
#region Public methods
/// Completes initialization (obsolete).
public void Recompute() {
//// Properties.Clear();
if ((this.ToneList == null) || (this.ToneList.Count <= 0))
{
return;
}
////SortToneArray();
this.validToneList = this.ToneList.ValidToneList;
this.realHarmonicState = null;
//// ?!? this.formalHarmonicState = null;
if (this.validToneList.Count > 0) {
//// // // list.Sort();
this.ComputeAmbit(); //// this.validToneList
this.ComputeIndexOfCentre(); //// this.validToneList
this.ComputeState(); //// this.validToneList
this.ComputeWeight(); //// this.validToneList
}
this.toneSchema = null;
}
/// Returns formal representation of the cluster.
/// Returns value.
public HarmonicStructure FormalStruct() {
this.ToneList.Where(mt => mt != null && mt.IsTrueTone).Aggregate(0L, (current, mt) => current | BinaryNumber.BitAt(mt.Pitch.Element));
var hS = HarmonicStructure.GetNewHarmonicStructure(this.HarmonicSystem, null); //// number
return hS;
}
#endregion
#region String representation
/// String representation of the object.
/// Returns value.
public override string ToString() {
var s = new StringBuilder();
//// s.AppendFormat("Harmonic Cluster");
//// s.Append(this.StringOfProperties()+"\n");
//// s.Append(harStruct.ToString(CultureInfo.CurrentCulture));
foreach (var mt in this.ToneList.Where(mt => mt != null))
{
s.Append(mt.ToShortString() + ",");
}
//// s.Append(StringOfProperties());
return s.ToString();
}
#endregion
#region Computation of properties
/// Sets properties derived from Ambit.
public void ComputeState() { ///// MusicalToneCollection melodicTones
//// Contract.Requires(melodicTones != null);
//// if (melodicTones == null) { return; }
if (this.HarmonicStructure == null) {
return;
}
//// HarmonicSystem hS = (HarmonicSystem)this.HarmonicStructure.HarmonicSystem;
if (this.ValidToneList.Count != 0) {
//// HarmonicStateReal state = new HarmonicStateReal(hS, melodicTones);
//// string key = this.ValidToneList.ToneKey;
var state = this.RealHarmonicState;
//// if (HarmonicStateReal.UsedStates.ContainsKey(key)) { state = HarmonicStateReal.UsedStates[key];
//// } else { state = this.RealHarmonicState; HarmonicStateReal.UsedStates[key] = state; }
this.RealEnergy.Continuity = state.RealContinuity; //// GetProperty(GenProperty.RealContinuity);
this.RealEnergy.Impulse = state.RealImpulse; //// GetProperty(GenProperty.RealImpulse);
this.RealEnergy.Potential = state.RealPotential; //// GetProperty(GenProperty.RealPotential);
this.RealEnergy.Consonance = state.RealConsonance; //// GetProperty(GenProperty.RealConsonance);
}
else {
this.RealEnergy.Consonance = 100f;
}
//// HarmonicStateFormal formalState = new HarmonicStateFormal(hS, HarmonicStructure);
//// 2019/05
if (this.HarmonicStructure.Level > 0) {
var formalState = this.FormalHarmonicState;
this.FormalEnergy.Continuity =
formalState.FormalContinuity; //// GetProperty(GenProperty.FormalContinuity);
this.FormalEnergy.Impulse = formalState.FormalImpulse; //// GetProperty(GenProperty.FormalImpulse);
this.FormalEnergy.Potential =
formalState.FormalPotential; //// GetProperty(GenProperty.FormalPotential);
this.FormalEnergy.Consonance =
formalState.FormalConsonance; //// GetProperty(GenProperty.FormalConsonance);
this.FormalEnergy.Balance = 0;
}
}
#endregion
#region Private methods
/// Determines estimated cluster extent.
/// Density of cluster.
private void DetermineExtent(float givenDensity) {
const float wideToMiddleBreak = 6.0f;
const float middleToTightBreak = 3.0f;
this.Extent = givenDensity > middleToTightBreak ? givenDensity > wideToMiddleBreak ? HarmonicClusterExtent.WideExtent : HarmonicClusterExtent.MiddleExtent : HarmonicClusterExtent.TightExtent;
}
/// Compute pitch center of the cluster.
private void ComputeIndexOfCentre() {
//// Contract.Requires(melodicTones != null);
var melodicTones = this.validToneList;
if (melodicTones == null || melodicTones.Count == 0) {
return;
}
float centralIndex;
if (melodicTones.Count > 1) {
float total = (from mt in melodicTones select mt.Pitch.SystemAltitude).Sum();
//// if (melodicTones.Count != 0) {
centralIndex = total / melodicTones.Count;
//// }
}
else {
centralIndex = melodicTones.First().Pitch.SystemAltitude;
}
this.IndexOfCentre = centralIndex;
}
/// Sets properties derived from Ambit.
private void ComputeWeight() {
//// Contract.Requires(melodicTones != null);
var melodicTones = this.validToneList;
if (melodicTones == null || melodicTones.Count == 0) {
return;
}
float weight = 0;
if (melodicTones.Count > 1) {
var total = (from mt in melodicTones select mt.Weight).Sum();
if (this.ToneList.Count > 0) {
weight = total / this.ToneList.Count;
}
}
else {
weight = 1.0f;
}
this.Weight = weight;
}
/// Sets properties derived from Ambit.
private void ComputeAmbit() {
//// Contract.Requires(melodicTones != null);
var melodicTones = this.validToneList;
if (melodicTones == null || melodicTones.Count == 0) {
return;
}
var sysLength = 0;
if (melodicTones.Count > 1) { //// melodicTones.Count != 0
var t0 = melodicTones.ElementAt(0);
var t1 = melodicTones.ElementAt(melodicTones.Count - 1);
sysLength = t0 != null && t1 != null && t0.IsTrueTone && t1.IsTrueTone ?
t1.Pitch.SystemAltitude - t0.Pitch.SystemAltitude :
0;
}
float realAmbit = sysLength;
var formalAmbit = this.HarmonicStructure?.HarmonicSystem.FormalLength(sysLength) ?? 0;
var density = realAmbit / melodicTones.Count; //// melodicTones.Count > 1 ? realAmbit / melodicTones.Count : 0;
this.Ambit = (int)realAmbit;
this.FormalAmbit = formalAmbit;
this.Density = density;
this.DetermineExtent(density);
}
#endregion
}
}